package com.xlhl.init.manage;

import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author xlhl
 */
@Component
public class CacheManage {
    @Resource
    private StringRedisManage redisManage;

    /**
     * 最小过期时间 30 分钟
     */
    private int minTimeoutSeconds = 60 * 30;

    /**
     * 最大过期时间 60 分钟
     */
    private int maxTimeoutSeconds = 60 * 60;

    /**
     * Caffeine 本地缓存
     */
    private Cache<String, String> caffeineCache =
            Caffeine.newBuilder()
                    .initialCapacity(1024)
                    .maximumSize(10000L)
                    // 缓存 30 分钟移除
                    .expireAfterWrite(30L, TimeUnit.MINUTES)
                    .build();

    /**
     * 设置最小超时时间（秒）
     *
     * @param minTimeoutSeconds
     */

    public void setMinTimeoutSeconds(int minTimeoutSeconds) {
        this.minTimeoutSeconds = minTimeoutSeconds;
    }

    /**
     * 设置最大超时时间（秒）
     *
     * @param maxTimeoutSeconds
     */
    public void setMaxTimeoutSeconds(int maxTimeoutSeconds) {
        this.maxTimeoutSeconds = maxTimeoutSeconds;
    }

    public void setCaffeineCache(Cache<String, String> cache) {
        this.caffeineCache = cache;
    }

    public void clearCache(String key) {
        redisManage.delete(key);
        caffeineCache.cleanUp();
    }

    /**
     * 获取缓存中的列表数据，如果未获取到数据，则调用 GetDataFunc 函数获取到源数据
     *
     * @param key
     * @param getDataFunc
     * @param dataClazz
     * @param <T>
     * @return
     */
    public <T> List<T> getList(String key, GetDataFunc getDataFunc, Class<T> dataClazz) {
        //  从本地缓存中获取数据
        String data = caffeineCache.getIfPresent(key);
        if (StrUtil.isNotBlank(data)) {
            return JSONArray.parseArray(data).toList(dataClazz);
        }
        //  从 Redis 中获取数据
        data = redisManage.get(key);
        if (StrUtil.isNotBlank(data)) {
            caffeineCache.put(key, data);
            return JSONArray.parseArray(data).toList(dataClazz);
        }

        //  从外部传入的方式获取数据
        data = getDataFunc.getData();
        //  数据入缓存
        caffeineCache.put(key, data);
        redisManage.set(key, data, RandomUtil.randomInt(minTimeoutSeconds, maxTimeoutSeconds + 1), TimeUnit.SECONDS);

        return JSONArray.parseArray(data).toList(dataClazz);
    }

    /**
     * 获取缓存中的数据，如果未获取到数据，则调用 GetDataFunc 函数获取到源数据
     *
     * @param key
     * @param getDataFunc
     * @param dataClazz
     * @param <T>
     * @return
     */
    public <T> T get(String key, GetDataFunc getDataFunc, TypeReference<T> dataClazz) {
        String data = caffeineCache.getIfPresent(key);
        if (StrUtil.isNotBlank(data)) {
            return JSONObject.parseObject(data, dataClazz);
        }

        data = redisManage.get(key);
        if (StrUtil.isNotBlank(data)) {
            caffeineCache.put(key, data);
            return JSONObject.parseObject(data, dataClazz);
        }

        data = getDataFunc.getData();

        caffeineCache.put(key, data);
        redisManage.set(key, data, RandomUtil.randomInt(minTimeoutSeconds, maxTimeoutSeconds + 1), TimeUnit.SECONDS);

        return JSONObject.parseObject(data, dataClazz);
    }

    @FunctionalInterface
    public interface GetDataFunc {
        /**
         * 获取数据
         * 需要自行转换为 JSON 字符串返回
         *
         * @return
         */
        String getData();
    }
}
